package com.ruge.core.exception;

import com.ruge.core.response.Response;
import com.ruge.core.tool.FormatTool;
import com.ruge.tool.str.StringTool;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;

import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author ruge.wu
 * @since 2022/1/4 13:42
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
    public GlobalExceptionHandler() {
    }

    @ExceptionHandler({Exception.class, Throwable.class})
    public final ResponseEntity<Object> handleExceptionGlobal(Throwable ex, WebRequest request, HandlerMethod handlerMethod) {
        this.addMethodProduces(request, handlerMethod);
        if (!(ex instanceof BaseBusinessException)) {
            try {
                return super.handleException((Exception) ex, request);
            } catch (Exception var7) {
                if (log.isWarnEnabled()) {
                    log.warn("Unknown exception type: " + var7.getClass().getName());
                }

                HttpStatus status = HttpStatus.INTERNAL_SERVER_ERROR;
                return this.handleExceptionInternal(var7, null, new HttpHeaders(), status, request);
            }
        } else {
            BaseBusinessException e = (BaseBusinessException) ex;
            IError error = e.getError();
            Response errorBody = new Response();
            errorBody.setStatus(Response.Status.FAILED);
            errorBody.setErrorCode(error.getErrorCode());
            errorBody.setErrorMessage(error.getErrorMessage());
            errorBody.setExtMessage(FormatTool.buildJson(e));
            return new ResponseEntity(errorBody, null, HttpStatus.OK);
        }
    }

    private void addMethodProduces(WebRequest request, HandlerMethod handlerMethod) {
        String[] produces = null;
        GetMapping getMapping = handlerMethod.getMethodAnnotation(GetMapping.class);
        if (null != getMapping) {
            produces = getMapping.produces();
        } else {
            PostMapping postMapping = handlerMethod.getMethodAnnotation(PostMapping.class);
            if (null != postMapping) {
                produces = postMapping.produces();
            }
        }

        if (null != produces) {
            Set<MediaType> mediaTypes = new HashSet(produces.length);
            String[] var6 = produces;
            int var7 = produces.length;

            for (int var8 = 0; var8 < var7; ++var8) {
                String produce = var6[var8];
                mediaTypes.add(MediaType.parseMediaType(produce));
            }

            request.setAttribute(HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE, mediaTypes, 0);
        }

    }

    @Override
    protected ResponseEntity<Object> handleExceptionInternal(Exception e, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
        if (HttpStatus.INTERNAL_SERVER_ERROR.equals(status)) {
            request.setAttribute("javax.servlet.error.exception", e, 0);
            log.error("", e);
        }

        Response errorBody = new Response();
        errorBody.setStatus(Response.Status.FAILED);
        errorBody.setErrorCode("SYS." + status.value());
        errorBody.setErrorMessage(status.getReasonPhrase());
        errorBody.setExtMessage(FormatTool.buildJson(e));
        return new ResponseEntity(errorBody, headers, HttpStatus.OK);
    }

    @Override
    protected ResponseEntity<Object> handleMethodArgumentNotValid(MethodArgumentNotValidException ex, HttpHeaders headers, HttpStatus status, WebRequest request) {
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        StringBuilder errMessage = StringTool.builder();
        errMessage.append("input parameters invalid:[");
        fieldErrors.forEach(e -> errMessage.append("{" + e.getField() + ":" + e.getDefaultMessage() + "}"));
        errMessage.append("]");
        Response errorBody = new Response();
        errorBody.setStatus(Response.Status.FAILED);
        errorBody.setErrorCode("SYS." + status.value());
        errorBody.setErrorMessage(status.getReasonPhrase());
        errorBody.setExtMessage(errMessage.toString());
        this.logger.error(errMessage);
        return new ResponseEntity(errorBody, HttpStatus.OK);
    }

}
